package com.ruoyi.workflow.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.workflow.enums.FlowModelStateEnum;
import com.ruoyi.workflow.enums.SuspensionStateEnum;
import com.ruoyi.workflow.exception.FlowModelException;
import com.ruoyi.workflow.mapper.FlowModelMapper;
import com.ruoyi.workflow.pojo.entity.FlowModel;
import com.ruoyi.workflow.pojo.entity.PearlFlowForm;
import com.ruoyi.workflow.pojo.param.SuspensionModelParams;
import com.ruoyi.workflow.pojo.query.ModelQuery;
import com.ruoyi.workflow.pojo.vo.FlowModelVO;
import com.ruoyi.workflow.pojo.vo.ProcessDefinitionVO;
import com.ruoyi.workflow.service.IFlowModelService;
import com.ruoyi.workflow.service.IPearlFlowFormService;
import com.ruoyi.workflow.util.ModelElementValidateUtils;
import com.ruoyi.workflow.validation.UserTaskValidator;
import org.apache.commons.io.IOUtils;
import org.camunda.bpm.engine.RepositoryService;
import org.camunda.bpm.engine.repository.Deployment;
import org.camunda.bpm.engine.repository.ProcessDefinition;
import org.camunda.bpm.engine.repository.ProcessDefinitionQuery;
import org.camunda.bpm.engine.repository.UpdateProcessDefinitionSuspensionStateBuilder;
import org.camunda.bpm.model.bpmn.Bpmn;
import org.camunda.bpm.model.bpmn.BpmnModelInstance;
import org.camunda.bpm.model.xml.validation.ModelElementValidator;
import org.camunda.bpm.model.xml.validation.ValidationResults;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.xml.stream.XMLStreamException;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;

/**
 * 流程模型扩展Service业务层处理
 *
 * @author ruoyi
 * @date 2023-12-11
 */
@Service
public class FlowModelServiceImpl implements IFlowModelService {

    @Autowired
    private FlowModelMapper flowModelMapper;

    @Autowired
    private RepositoryService repositoryService;

    @Autowired
    private IPearlFlowFormService flowFormService;


    public List<ProcessDefinitionVO> list(ModelQuery params) throws XMLStreamException {
        // 1. 定义条件
        ProcessDefinitionQuery query = repositoryService.createProcessDefinitionQuery();
        if (StrUtil.isNotEmpty(params.getName())) {
            query.processDefinitionName(params.getName()); // 名称
        }
        if (params.isLatestVersion()) {
            query.latestVersion();// 最后一个版本
        }
        // 2. 执行查询
        List<ProcessDefinition> list = query.list();
        // 3. 对象转换
        List<ProcessDefinitionVO> result = new ArrayList<>();
        if (CollUtil.isNotEmpty(list)) {
            for (ProcessDefinition processDefinition : list) {
                ProcessDefinitionVO vo = new ProcessDefinitionVO();
                vo.setKey(processDefinition.getKey());
                vo.setVersion(processDefinition.getVersion());
                vo.setHistoryLevel(processDefinition.getHistoryTimeToLive());
                vo.setHistoryTimeToLive(processDefinition.getHistoryTimeToLive());
                vo.setVersion(processDefinition.getVersion());
                result.add(vo);
            }
        }
        return result;
    }

    /**
     * 查询流程模型扩展
     *
     * @param id 流程模型扩展主键
     * @return 流程模型扩展
     */
    @Override
    public FlowModel selectFlowModelById(Long id) {
        return flowModelMapper.selectFlowModelById(id);
    }

    /**
     * 查询流程模型扩展列表
     *
     * @param flowModel 流程模型扩展
     * @return 流程模型扩展
     */
    @Override
    public List<FlowModelVO> selectFlowModelList(FlowModel flowModel) {
        return flowModelMapper.selectFlowModelList(flowModel);
    }

    /**
     * 新增流程模型扩展
     *
     * @param flowModel 流程模型扩展
     * @return 结果
     */
    @Override
    public Long insertFlowModel(FlowModel flowModel) {
        if (flowModel.getId() != null) {
            // 新增待发布模型
            FlowModel selectFlowModelById = flowModelMapper.selectFlowModelById(flowModel.getId());
            selectFlowModelById.setEditorXml(flowModel.getEditorXml());
            if (FlowModelStateEnum.PUBLISHED.getCode().equals(selectFlowModelById.getModelState())) {
                // 当前已发布，说明是新版（新增一条）
                selectFlowModelById.setId(null);
                selectFlowModelById.setVersion(selectFlowModelById.getVersion() + 1);
                selectFlowModelById.setModelState(FlowModelStateEnum.WAIT_PUBLISH.getCode());
                selectFlowModelById.setCreateBy(SecurityUtils.getUsername());
                selectFlowModelById.setCreateTime(DateUtils.getNowDate());
                flowModelMapper.insertFlowModel(selectFlowModelById);
            } else {
                // 未发布 直接修改
                selectFlowModelById.setModelState(FlowModelStateEnum.WAIT_PUBLISH.getCode());
                flowModelMapper.updateFlowModel(selectFlowModelById);
            }
        } else {
            // 新增草稿
            flowModel.setModelState(FlowModelStateEnum.DRAFT.getCode());
            flowModel.setCreateBy(SecurityUtils.getUsername());
            flowModel.setCreateTime(DateUtils.getNowDate());
            flowModel.setVersion(1);
            flowModelMapper.insertFlowModel(flowModel);
        }
        return flowModel.getId();


    }

    /**
     * 修改流程模型扩展
     *
     * @param flowModel 流程模型扩展
     * @return 结果
     */
    @Override
    public int updateFlowModel(FlowModel flowModel) {
        flowModel.setUpdateTime(DateUtils.getNowDate());
        return flowModelMapper.updateFlowModel(flowModel);
    }

    /**
     * 批量删除流程模型扩展
     *
     * @param ids 需要删除的流程模型扩展主键
     * @return 结果
     */
    @Override
    public int deleteFlowModelByIds(Long[] ids) {
        return flowModelMapper.deleteFlowModelByIds(ids);
    }

    /**
     * 删除流程模型扩展信息
     *
     * @param id 流程模型扩展主键
     * @return 结果
     */
    @Override
    public int deleteFlowModelById(Long id) {
        return flowModelMapper.deleteFlowModelById(id);
    }

    @Override
    public void publish(Long id) {
        // 1. 查询自定义模型表
        FlowModel flowModel = flowModelMapper.selectFlowModelById(id);

        // 2. 校验
        if (null == flowModel) {
            throw new ServiceException("当前模型不存在");
        }
        String editorXml = flowModel.getEditorXml(); // 未发布的在编模型
        if (StrUtil.isEmpty(editorXml)) {
            throw new ServiceException("模型文件不存在");
        }

        // 3. 读取为模型对象并校验
        InputStream inputStream = new ByteArrayInputStream(editorXml.getBytes());
        BpmnModelInstance bpmnModelInstance = Bpmn.readModelFromStream(inputStream);
        ModelElementValidateUtils.validate(bpmnModelInstance); // 自定义校验

        // 4. 部署
        Deployment deployment = repositoryService.createDeployment()
                .name(flowModel.getName())
                .tenantId(flowModel.getTenantId())
                .addModelInstance(flowModel.getDefKey() + ".bpmn", bpmnModelInstance)
                .deploy();

        // 5. 修改扩展表
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().deploymentId(deployment.getId()).singleResult();
        flowModel.setSuspensionState(SuspensionStateEnum.ACTIVATION.getCode());
        flowModel.setModelState(FlowModelStateEnum.PUBLISHED.getCode());
        flowModel.setDefId(processDefinition.getId());
        flowModel.setDefKey(processDefinition.getKey());
        flowModel.setUpdateTime(DateUtils.getNowDate());
        flowModel.setEditorXml("");
        flowModel.setVersion(processDefinition.getVersion());
        flowModel.setPublishTime(DateUtils.getNowDate());
        flowModel.setUpdateBy(SecurityUtils.getUsername());
        flowModelMapper.updateFlowModel(flowModel);
    }

    @Override
    public String getXML(Long id) throws IOException {
        FlowModel flowModel = flowModelMapper.selectFlowModelById(id);
        // 草稿
        Integer modelState = flowModel.getModelState();
        if (FlowModelStateEnum.DRAFT.getCode().equals(modelState)) {
            return StrUtil.EMPTY;
        }
        // 未发布
        String editorXml = flowModel.getEditorXml();
        if (StrUtil.isNotEmpty(editorXml)) {
            return editorXml;
        }
        // 已发布
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
                .processDefinitionId(flowModel.getDefId())
                .singleResult();
        InputStream inputStream = repositoryService.getResourceAsStream(processDefinition.getDeploymentId(), processDefinition.getResourceName());
        return IOUtils.toString(inputStream, StandardCharsets.UTF_8);
    }

    @Override
    @Transactional
    public void suspension(SuspensionModelParams params) {
        FlowModel flowModel = flowModelMapper.selectFlowModelById(params.getModelId());
        if (null == flowModel) {
            throw new ServiceException("当前模型不存在");
        }

        // 根据ID操作（实际场景可以根据KEY挂起多个版本，挂起当前定义下的所有流程实例）
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
                .processDefinitionId(flowModel.getDefId())
                .singleResult();
        UpdateProcessDefinitionSuspensionStateBuilder stateBuilder = repositoryService.updateProcessDefinitionSuspensionState().byProcessDefinitionId(processDefinition.getId());

        // 挂起 or 激活
        if (SuspensionStateEnum.ACTIVATION.getCode().equals(params.getSuspensionState())) {
            stateBuilder.activate();
            flowModel.setSuspensionState(SuspensionStateEnum.ACTIVATION.getCode());
        } else if (SuspensionStateEnum.SUSPENSION.getCode().equals(params.getSuspensionState())) {
            stateBuilder.suspend();
            flowModel.setSuspensionState(SuspensionStateEnum.SUSPENSION.getCode());
        } else {
            throw new ServiceException("参数错误");
        }
        flowModelMapper.updateFlowModel(flowModel);
    }


    @Override
    public PearlFlowForm getStartFormById(Long id) {
        FlowModel flowModel = flowModelMapper.selectFlowModelById(id);
        if (null == flowModel) {
            throw new ServiceException("当前模型不存在");
        }

        Long formId = flowModel.getFormId();
        if (ObjectUtil.isNull(formId)) {
            throw new ServiceException("当前模型未绑定表单");
        }

        PearlFlowForm form = flowFormService.selectPearlFlowFormById(formId);
        if (ObjectUtil.isNull(form)) {
            throw new ServiceException("当前表单不存在");
        }

        return form;
    }

    @Override
    public void validate(FlowModel flowMode) {
        // 1. XML
        String editorXml = flowMode.getEditorXml();
        if (StrUtil.isEmpty(editorXml)) {
            throw new FlowModelException("XML不能为空");
        }

        // 2. 读取为模型对象并校验
        InputStream inputStream = new ByteArrayInputStream(editorXml.getBytes());
        BpmnModelInstance bpmnModelInstance = Bpmn.readModelFromStream(inputStream);
        ModelElementValidateUtils.validate(bpmnModelInstance); // 自定义校验
    }
}
